package com.yunji.order.business;

import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableTable;
import com.google.common.collect.Lists;
import com.yunji.base.bo.BelongAmountModifyBO;
import com.yunji.base.event.GuavaEventBus;
import com.yunji.base.service.BelongService;
import com.yunji.cabinet.cache.CabinetCache;
import com.yunji.cabinet.device.DeviceCommandEngine;
import com.yunji.cabinet.service.ICabinetService;
import com.yunji.common.base.BaseBusiness;
import com.yunji.common.cache.LockUtil;
import com.yunji.common.constant.HTTPStatus;
import com.yunji.common.constant.LockCacheConstant;
import com.yunji.common.enums.business.TradeTypes;
import com.yunji.common.event.notice.SellProductNoticeEvent;
import com.yunji.common.exception.ServiceException;
import com.yunji.common.model.cache.CabinetCacheDTO;
import com.yunji.common.model.cache.ProductAgentCacheDTO;
import com.yunji.common.model.cache.ProductBelongCacheDTO;
import com.yunji.common.model.cache.ProductCacheDTO;
import com.yunji.common.utils.DecimalUtils;
import com.yunji.common.utils.JsonUtil;
import com.yunji.common.utils.Pair;
import com.yunji.common.utils.bean.BeanUtils;
import com.yunji.common.utils.uuid.UUIDUtils;
import com.yunji.coupon.bo.CouponBO;
import com.yunji.coupon.bo.OrderAmtBO;
import com.yunji.coupon.business.CouponBusiness;
import com.yunji.coupon.dto.OrderAmtDTO;
import com.yunji.coupon.dto.OrderAndCouponDTO;
import com.yunji.coupon.engine.CouponConvert;
import com.yunji.coupon.engine.CouponRuleEngine;
import com.yunji.coupon.service.CouponService;
import com.yunji.coupon.vo.CouponVO;
import com.yunji.order.bo.*;
import com.yunji.order.command.order.OrderStateMachine;
import com.yunji.order.context.builder.OrderBuilder;
import com.yunji.order.context.convert.OrderConvert;
import com.yunji.order.context.convert.RefundConvert;
import com.yunji.order.context.engine.OrderCalculate;
import com.yunji.order.context.validate.OrderValidate;
import com.yunji.order.domain.OrderDO;
import com.yunji.order.domain.OrderDetailDO;
import com.yunji.order.domain.OrderScaleDO;
import com.yunji.order.dto.OrderCreateDTO;
import com.yunji.order.dto.OrderDetailDTO;
import com.yunji.order.dto.OrderDetailRefundDTO;
import com.yunji.order.dto.OrderPageDTO;
import com.yunji.order.dto.cmd.OrderCancelCommandDTO;
import com.yunji.order.dto.cmd.OrderCreateCommandDTO;
import com.yunji.order.dto.cmd.OrderPaidCommandDTO;
import com.yunji.order.emuns.*;
import com.yunji.order.service.OrderScaleService;
import com.yunji.order.service.OrderService;
import com.yunji.order.service.RefundService;
import com.yunji.order.vo.OrderDetailVO;
import com.yunji.order.vo.OrderVO;
import com.yunji.order.wechat.WechatCallbackDTO;
import com.yunji.order.wechat.WechatTrade;
import com.yunji.product.cache.ProductCache;
import com.yunji.system.context.SystemContext;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author : peter-zhu
 * @date : 2025/3/29 14:00
 * @description : 订单业务处理 我要让订单变得更简单
 */
@Component
public class OrderBusiness extends BaseBusiness {
    @Resource
    private WechatBusiness wechatBusiness;
    @Resource
    private BelongService belongService;
    @Resource
    private OrderService orderService;
    @Resource
    private RefundService refundService;
    @Resource
    private ICabinetService cabinetService;
    @Resource
    private CouponBusiness couponBusiness;
    @Resource
    private OrderScaleService orderScaleService;
    @Value("${codePrefix.order:D}")
    private String codePrefix;
    @Resource
    private CouponService couponService;

    public Long createOrder(OrderCreateDTO dto) {
        logs.info("【商品订单】购买信息：{}", dto);
        CabinetCacheDTO cabinet = CabinetCache.getCabinet(cabinetService.getImeiByQrcode(dto.getQrcode()));
        ExistsOrderBO build = ExistsOrderBO.builder().
                orderStatus(OrderStatusEnum.WAIT_PAYING.getCode()).containerId(cabinet.getContainerId()).build();
        boolean existsOrder = orderService.existsOrder(build);
        if (existsOrder) {
            Long orderId = orderService.getOrderId(build);
            throw new ServiceException(HTTPStatus.BUSINESS, orderId.toString());
        }
        List<OrderDetailDTO> orderDetailDTOs = dto.getOrderDetails();
        OrderAmtBO orderAndDetailAmt = getOrderAndDetailAmt(cabinet, orderDetailDTOs, dto.getCouponId());
        OrderDO order = OrderBuilder.builder().
                buildMember().buildContainer(cabinet).buildAmt(orderAndDetailAmt.getOrderAmt()).build();
        List<OrderDetailDO> orderDetailList = OrderConvert.convert2DetailDO(orderAndDetailAmt.getOrderDetailsAmt());
        if (orderDetailList.size() != orderDetailDTOs.size()) {
            logs.error("OrderBusiness createOrder error,length not equals orderDetailDTOs.size,dto{},domain:{}",
                    JsonUtil.toJsonString(orderDetailDTOs), JsonUtil.toJsonString(orderDetailList));
            throw new ServiceException("订单明细长度不一致，请联系管理员查看日志");
        }
        order.setPayMode(PayMode.WECHAT.getCode());
        order.setOrderCode(UUIDUtils.orderCode(codePrefix));
        order.setOrderStatus(OrderStatusEnum.WAIT_PAYING.getCode());
        order.setProductNums(dto.getOrderDetails().stream().map(OrderDetailDTO::getProductNum).reduce(Integer::sum)
                .orElseThrow(() -> new ServiceException("购买数量为null或0")));
        logs.info("【商品订单】订单信息：{}", order);
        OrderStateMachine.statusChange(new OrderCreateCommandDTO(order, orderDetailList, OrderStatusEnum.WAIT_PAYING, dto.getCouponId()));
        return order.getOrderId();
    }

    public WechatTrade pay(Long orderId) {
        OrderDO order = orderService.getOrderById(orderId);
        ServiceException.throwException(order == null, "订单不存在");
        DeviceCommandEngine.validateOnline(order.getChipImei());
        boolean lock = LockUtil.tryLock(LockCacheConstant.ORDER_LOCK + orderId, 20, 60);
        List<OrderDetailDO> orderDetails = orderService.getOrderDetailsByOid(orderId);
        OrderValidate.orderPayValidate(order.getChipImei(), orderDetails);
        try {
            if (lock) {
                BigDecimal tradeAmount = order.getActualAmount();
                Long amount = WechatBusiness.multiply100(tradeAmount);
                String openId = SystemContext.getUser().getOpenId();
                logs.info("【微信购买】用户微信OpenId:{}，交易金额{}分,订单号：{}", openId, amount, order.getOrderId());
                List<String> productNames = orderDetails.stream().map(OrderDetailDO::getProductName).collect(Collectors.toList());
                PayableBO wechat = new PayableBO(openId, order.getOrderCode(), Joiner.on(',').join(productNames), amount);
                WechatTrade wechatTrade = wechatBusiness.createTrade(wechat);
                ServiceException.throwException(wechatTrade == null, "微信交易失败");
                //OrderStateMachine.statusChange(new OrderCommandDTO(order, OrderStatusEnum.PAID));
                GuavaEventBus.post(new SellProductNoticeEvent(SystemContext.getUser(), orderId));
                return wechatTrade;
            }
            return null;
        } catch (Exception e) {
            logs.error("【微信购买】支付订单失败{}", order, e);
            throw new ServiceException("支付订单失败:" + order.getOrderCode());
        } finally {
            LockUtil.unlock(LockCacheConstant.ORDER_LOCK + orderId);
        }
    }

    public Map<String, Object> paidCallback(HttpServletRequest request) {
        WechatCallbackDTO rs = wechatBusiness.payCallback(request);
        if (rs.isSuccess()) {
            OrderDO order = orderService.getOrderByCode(rs.getTradeCode());
            boolean lock = LockUtil.tryLock(LockCacheConstant.ORDER_LOCK + order.getOrderId(), 20, 60);
            try {
                if (lock)
                    OrderStateMachine.statusChange(new OrderPaidCommandDTO(order, OrderStatusEnum.PAY_SUCCESS, rs));
            } finally {
                LockUtil.unlock(LockCacheConstant.ORDER_LOCK + order.getOrderId());
            }
        }
        return WechatBusiness.buildSuccess();
    }

    public void cancelOrder(Long orderId, OrderCancelTypeEnum closeTypeEnum) {
        boolean lock = LockUtil.tryLock(LockCacheConstant.ORDER_LOCK + orderId, 20, 60);
        try {
            if (lock) {
                OrderDO currentOrder = orderService.getOrderById(orderId);
                ServiceException.throwException(currentOrder == null, "未找到订单信息,关闭失败");
                if (currentOrder.getOrderStatus().equals(OrderStatusEnum.WAIT_PAYING.getCode()))
                    OrderStateMachine.statusChange(new OrderCancelCommandDTO(currentOrder, OrderStatusEnum.ORDER_CANCEL, closeTypeEnum));
            }
        } finally {
            LockUtil.unlock(LockCacheConstant.ORDER_LOCK + orderId);
        }
    }

    public List<OrderDO> orderScopeList(OrderPageDTO pageDTO) {
        List<OrderDO> list = orderService.orderScopeList(pageDTO);
        boolean plate = SystemContext.isPlate();
        List<Long> orderIds = list.stream().map(OrderDO::getOrderId).collect(Collectors.toList());
        List<OrderDetailRefundDTO> refundStatusList = refundService.getStatusNumsByOid(orderIds);
        ImmutableTable<String, Long, List<Pair<Integer, Integer>>> tab = RefundConvert.buildTableNums(refundStatusList);
        for (OrderDO o : list) {
            if (!plate) {
                o.setMemberPhone(OrderConvert.encryptMemberPhone(o.getMemberPhone()));
                o.setCostPrice(null);
                ImmutableMap<Long, List<Pair<Integer, Integer>>> row = tab.row(o.getOrderCode());
                if (row != null)
                    o.setRefundStatusNums(row.values().stream()
                            .flatMap(List::stream)
                            .collect(Collectors.toList()));
            }
        }
        return list;
    }

    public List<OrderVO> appGetOrderList(ListOrderMemberBO order) {
        order.setMemberId(SystemContext.getUserId());
        order.setReadStatus(ReadStatus.READ.getCode());
        List<OrderDO> orderDOS = orderService.orderListByMember(order);
        List<Long> orderIds = orderDOS.stream().map(OrderDO::getOrderId).collect(Collectors.toList());
        List<OrderDetailRefundDTO> refundStatusList = refundService.getStatusNumsByOid(orderIds);
        return OrderConvert.domains2VOs(orderDOS, orderService, refundStatusList);
    }

    public OrderVO appGetOrder(Long id) {
        OrderDO orderDO = orderService.getOrderById(id);
        ServiceException.throwException(orderDO == null, "订单不存在");
        List<OrderDetailRefundDTO> refundStatusList = refundService.getStatusNumsByOid(Lists.newArrayList(id));
        List<OrderDetailDO> orderDetails = orderService.getOrderDetailsByOid(orderDO.getOrderId());
        return OrderConvert.domain2VO(orderDO, orderDetails, refundStatusList);
    }

    public List<OrderDO> searchOrderStatus(Integer... status) {
        return orderService.searchOrderStatus(status);
    }

    public List<OrderDO> sendProductOrder(ListDeliveryOrderBO bo) {
        return orderService.listDeliveryOrder(bo);
    }

    public List<OrderDO> searchScaleOrder(ListScaleOrderBO bo) {
        return orderService.listScaleOrder(bo);
    }

    public OrderDO getOrderAndDetailNums(Long id) {
        OrderDO order = orderService.getOrderById(id);
        if (BeanUtils.isNull(order)) {
            return null;
        }
        order.setCouponName(couponService.getCouponNameByOid(order.getOrderId()));
        if (!SystemContext.isPlate())
            order.setMemberName(OrderConvert.encryptMemberPhone(order.getMemberPhone()));
        List<OrderDetailRefundDTO> refundStatusList = refundService.getStatusNumsByOid(Lists.newArrayList(id));
        List<OrderDetailVO> orderDetailVOS = OrderConvert.detailDO2VO(orderService.getOrderDetailsByOid(order.getOrderId()));
        OrderConvert.setDetailsNums(order.getOrderCode(), orderDetailVOS, refundStatusList);
        order.setDetailList(orderDetailVOS);
        return order;
    }

    public OrderDO getOrderById(Long id) {
        return orderService.getOrderById(id);
    }

    public OrderDO getOrderByCode(String orderCode) {
        return orderService.getOrderByCode(orderCode);
    }

    public void updateOrder(OrderDO order) {
        ServiceException.throwException(!orderService.updateOrder(order), "更新失败");
    }

    /**
     * @author : peter-zhu
     * @date : 2025/3/29 13:48
     * @description : 解冻订单的分润金额
     */
    @Transactional
    public void unFrozenScale(OrderUnFrozenBO orderScale) {
        List<OrderScaleDO> orderScaleList = orderScaleService.getOrderScaleList
                (OrderScaleBO.builder().orderId(orderScale.getOrderId()).
                        scaleTypeList(Lists.newArrayList(TradeTypes.AGENT_SCALE.getCode(), TradeTypes.SITE_SCALE.getCode())).build());
        if (orderScaleList.size() != 2) {
            logs.error("order frozen data:{} duplicate: the expected value is 2, but the actual value is {}",
                    orderScale, orderScaleList.size());
            return;
        }
        Long orderId = orderScale.getOrderId();
        if (!OrderStatusEnum.PAY_SUCCESS.getCode().equals(orderScale.getOrderStatus())) {
            logs.error("order frozen data:{} have not finished", orderScale);
            return;
        }
        for (OrderScaleDO scaleDO : orderScaleList) {
            belongService.updateAccount(BelongAmountModifyBO.builder().belongId(scaleDO.getAccountId())
                    .frozenAmount(scaleDO.getScaleAmount().negate()).remainAmount(scaleDO.getScaleAmount()).build());
        }
        OrderDO update = new OrderDO();
        update.setOrderId(orderId);
        update.setFrozen(OrderFrozenEnum.FROZEN.getCode());
        orderService.updateOrder(update);
    }

    public void orderDeliveries(OrderDO order) {
        logs.info("【订单发货】订单信息:{}", order);
        wechatBusiness.wechatLogistics(order);
        Long orderId = order.getOrderId();
        OrderDO update = new OrderDO();
        update.setOrderId(orderId);
        update.setSendProduct(SendStatus.DELIVERED.getCode());
        Assert.isTrue(orderService.updateOrder(update), "发货失败");
    }

    public void deleteMemberOrder(Long orderId) {
        OrderDO update = new OrderDO();
        update.setOrderId(orderId);
        update.setReadStatus(ReadStatus.UNREAD.getCode());
        orderService.updateOrder(update);
    }

    public OrderAmtBO getOrderAmt(Long cabinetId, List<OrderDetailDTO> orderDetails, Long couponId) {
        CabinetCacheDTO cabinet = CabinetCache.getCabinet(cabinetService.getImeiById(cabinetId));
        return getOrderAmt(cabinet, orderDetails, couponId);
    }

    public OrderAmtBO getOrderAmt(CabinetCacheDTO byId, List<OrderDetailDTO> orderDetails, Long couponId) {
        return getOrderAndDetailAmt(byId, orderDetails, couponId);
    }

    public OrderAmtBO getOrderAndDetailAmt(CabinetCacheDTO byId, List<OrderDetailDTO> orderDetails, Long couponId) {
        OrderAmtDTO orderAmt = new OrderAmtDTO();
        List<OrderAmtDTO> detailAmtList = Lists.newArrayList();
        orderAmt.setSiteId(byId.getSiteId());
        // 先算出订单明细统计金额
        for (OrderDetailDTO orderDetail : orderDetails) {
            ProductBelongCacheDTO pb = ProductCache.getProductBelong(byId.getBelongId(), orderDetail.getProductId());
            ProductAgentCacheDTO agentPrice = pb.getProductAgentDTO();
            ProductCacheDTO productDTO = pb.getProductDTO();
            ServiceException.throwException(productDTO == null || agentPrice == null, "订单明细没有找到商品");
            OrderAmtDTO detailAmt = OrderCalculate.calculateOrderAmt(productDTO.getPrice(), productDTO.getCostPrice(),
                    agentPrice.getDiscountPrice(), orderDetail.getProductNum());
            detailAmt.setProductNums(orderDetail.getProductNum());
            detailAmt.setCostPrice(productDTO.getCostPrice());
            productDTO.setDiscountPrice(agentPrice.getDiscountPrice());
            detailAmt.setProductCacheDTO(productDTO);
            detailAmtList.add(detailAmt);
            orderAmt.add(detailAmt);
        }
        if (couponId != null) {
            CouponBO couponBO = couponBusiness.getCouponBO(couponId);
            CouponVO couponVO = CouponConvert.convert2CouponVO(couponBO);
            BigDecimal couponAmt = CouponRuleEngine.getCouponAmt(couponBO.getTmplDO().getDiscountType(),
                    orderAmt.getDiscountAmount(),
                    couponVO.getDiscountValue());
            orderAmt.setCoupleAmount(couponAmt);
            CouponRuleEngine.setAmtByCoupon(orderAmt);
            // 根据统计金额计算比例 算出优惠券金额再去乘明细占的比例
            BigDecimal sumDetailAmt = BigDecimal.ZERO;
            for (OrderAmtDTO amtDetail : detailAmtList) {
                BigDecimal ratio = amtDetail.getPayableAmount().divide(orderAmt.getPayableAmount(), 4, RoundingMode.DOWN);
                BigDecimal couponRatioAmt = couponAmt.multiply(ratio).setScale(2, RoundingMode.DOWN);
                BigDecimal subtract = amtDetail.getPayableAmount().subtract(couponRatioAmt);
                amtDetail.setPayableAmount(subtract.compareTo(BigDecimal.ZERO) > 0 ? subtract : BigDecimal.ZERO);
                sumDetailAmt = sumDetailAmt.add(amtDetail.getPayableAmount());
            }
            // 统计按优惠券扣除后比例的明细金额 然后订单实付金额减去扣除优惠券金额比例的明细 取余数后给第一个明细金额再加上
            BigDecimal remainder = orderAmt.getPayableAmount().subtract(sumDetailAmt);
            OrderAmtDTO orderAmtDTO = detailAmtList.stream().findFirst().orElse(new OrderAmtDTO());
            orderAmtDTO.setPayableAmount(orderAmtDTO.getPayableAmount().add(remainder));
        }
        return new OrderAmtBO(orderAmt, detailAmtList);
    }

    public CouponVO getOptimalCoupon(OrderAndCouponDTO dto) {
        OrderAmtDTO optimalCouponVO = getOrderAmt(dto.getCabinetId(), dto.getOrderDetails(), null).getOrderAmt();
        BigDecimal discountPrice = optimalCouponVO.getDiscountAmount();
        List<CouponBO> couponBOS = couponBusiness.unUsedSelectBOList(optimalCouponVO.getSiteId());
        CouponVO couponVO = null;
        if (CollectionUtils.isEmpty(couponBOS)) {
            return null;
        }
        for (CouponBO b : couponBOS) {
            if (b.isAvailable()) {
                BigDecimal amt = CouponRuleEngine.getCouponAmt(b.getTmplDO().getDiscountType(), discountPrice, b.getTmplDO().getDiscountValue());
                if (DecimalUtils.more(amt, optimalCouponVO.getCoupleAmount())) {
                    couponVO = CouponConvert.convert2CouponVO(b);
                    optimalCouponVO.setCoupleAmount(amt);
                    couponVO.setAmount(amt);
                }
            }
        }
        CouponRuleEngine.setAmtByCoupon(optimalCouponVO);
        return couponVO;
    }

    public CouponVO getSelectCoupon(Long couponId) {
        return CouponConvert.convert2CouponVO(couponBusiness.getCouponBO(couponId));
    }
}
